package com.tinem.platform.web.gateway.filter;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.tinem.platform.module.commons.util.SessionUtil;
import com.tinem.platform.module.pojo.co.Commons;
import com.tinem.platform.module.pojo.enums.common.Enabled;
import com.tinem.platform.module.pojo.vo.error.UserException;
import com.tinem.platform.module.starter.jpa.entity.config.ConfigWebGatewayUrlAuthorize;
import com.tinem.platform.module.starter.jpa.repository.config.ConfigWebGatewayUrlAuthorizeRepository;
import com.tinem.platform.module.starter.sdk.MessageCode;
import com.tinem.platform.web.gateway.util.GatewayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;

/**
 * @author fengzhihao
 * @version v1
 * @program: platform
 * @className ChannelScopeGatewayFilterFactory
 * @description 客户端请求签名
 * @site 
 * @company 
 * @create 2020-07-03 12:26
 */
@Slf4j
@Component
public class AuthorizeGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> implements ApplicationRunner {

    Cache<String, String> lfuCache = CacheUtil.newLFUCache(100000);

    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Resource
    ConfigWebGatewayUrlAuthorizeRepository configWebGatewayUrlAuthorizeRepository;

    AntPathMatcher antPathMatcher = new AntPathMatcher();
    List<ConfigWebGatewayUrlAuthorize> configWebGatewayUrlAuthorizeList;

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {

            String service = GatewayUtil.getApiService(exchange);
            String method = GatewayUtil.getApiMethod(exchange);
            String authorize = lfuCache.get(service+"&"+method);
            // 不需要权限
            if(Commons.NULL.equals(authorize)){
                return chain.filter(exchange);
            }

            String jit = GatewayUtil.getJit(exchange);
            String jitRedisKey = SessionUtil.getRedisKeySessionId(jit);
            String redisKeySessionIdAuthorize = SessionUtil.getRedisKeySessionIdAuthorize(jitRedisKey);
            // 如果命中缓存
            if(StrUtil.isNotEmpty(authorize)){
                if(stringRedisTemplate.opsForSet().isMember(redisKeySessionIdAuthorize,authorize)){
                    return chain.filter(exchange);
                }
                if(stringRedisTemplate.opsForSet().isMember(redisKeySessionIdAuthorize, Commons.ROOT_AUTHORIZE)){
                    return chain.filter(exchange);
                }
                log.warn("访问未授权地址: service:({});method:({});jit:({});authorize:({});",
                        service,
                        method,
                        jit,
                        authorize
                );
                throw new UserException(MessageCode.ERROR_SESSION_NOT_AUTHORIZE);
            }
            // 数据库规则匹配
            Optional<ConfigWebGatewayUrlAuthorize> optional = configWebGatewayUrlAuthorizeList.stream()
                    .filter(configUrlLogin -> ReUtil.isMatch(configUrlLogin.getService(),service))
                    .filter(configUrlLogin -> antPathMatcher.match(configUrlLogin.getMethod(),method))
                    .findFirst();
            // 数据库没有匹配规则
            if(!optional.isPresent()){
                // 记录此接口不需要权限
                lfuCache.put(service+"&"+method,Commons.NULL);
                return chain.filter(exchange);
            }
            // 数据库有匹配规则
            ConfigWebGatewayUrlAuthorize configUrlAuthorize = optional.get();
            authorize = optional.get().getAuthorize();
            // 记录此接口需要的权限
            lfuCache.put(service+"&"+method,authorize);
            if(stringRedisTemplate.opsForSet().isMember(redisKeySessionIdAuthorize,authorize)){
                return chain.filter(exchange);
            }
            if(stringRedisTemplate.opsForSet().isMember(redisKeySessionIdAuthorize, Commons.ROOT_AUTHORIZE)){
                return chain.filter(exchange);
            }
            log.warn("访问未授权地址: service:({});method:({});jit:({});authorize:({});",
                    service,
                    method,
                    jit,
                    authorize
                    );
            throw new UserException(MessageCode.ERROR_SESSION_NOT_AUTHORIZE);
        };
    }
    @Scheduled(cron = "0 */5 * * * ?")
    public void refresh(){
        configWebGatewayUrlAuthorizeList = configWebGatewayUrlAuthorizeRepository.findByStatus(Enabled.ENABLED);
    }

    public void clearCache(){
        lfuCache = CacheUtil.newLFUCache(100000);
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        refresh();
    }
}
